package validator

import (
	"bytes"
	"encoding/json"
	"fmt"

	rippleLib "github.com/rubblelabs/ripple/data"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/crypto"
	"github.com/wanchain/go-mpc/log"
)

type XrpTxHandler struct {
	PKBytes          hexutil.Bytes           `json:"pk"`
	TxHash           hexutil.Bytes           `json:txHash`
	TxData           string                  `json:txData`      //  raw tx of the chain
	ChainType        string                  `json:"chainType"` // 'WAN' or 'ETH' xrp
	TxId             hexutil.Bytes           `json:txId`
	XrpTx            rippleLib.Payment       `json:",omitempty"`
	XrpAccountDelete rippleLib.AccountDelete `json:",omitempty"`
	XrpAccountSet    rippleLib.AccountSet    `json:",omitempty"`
	XrpTrustSet      rippleLib.TrustSet      `json:",omitempty"`
	XrpType          rippleLib.TransactionType
}

func ParseXrp(s []byte, tx *XrpTxHandler) error {

	base := rippleLib.TxBase{}
	err := json.Unmarshal(s, &base)
	if err != nil {
		return err
	}
	if base.TransactionType == rippleLib.ACCOUNT_DELETE {
		err := json.Unmarshal(s, &tx.XrpAccountDelete)
		if err != nil {
			return err
		}
		tx.XrpType = rippleLib.ACCOUNT_DELETE
	} else if base.TransactionType == rippleLib.TRUST_SET {
		err := json.Unmarshal(s, &tx.XrpTrustSet)
		if err != nil {
			return err
		}
		tx.XrpType = rippleLib.TRUST_SET
	} else if base.TransactionType == rippleLib.ACCOUNT_SET {
		err := json.Unmarshal(s, &tx.XrpAccountSet)
		if err != nil {
			return err
		}
		tx.XrpType = rippleLib.ACCOUNT_SET
	} else {
		err := json.Unmarshal(s, &tx.XrpTx)
		if err != nil {
			return err
		}
		tx.XrpType = rippleLib.PAYMENT
	}

	return nil

}

func (tx XrpTxHandler) GetKey() []byte {

	if tx.XrpType == rippleLib.PAYMENT {
		var buffer bytes.Buffer
		buffer.Write(tx.PKBytes)
		buffer.Write(tx.XrpTx.Account.Bytes())
		buffer.Write(tx.XrpTx.Destination.Bytes())
		buffer.Write(tx.XrpTx.Amount.Bytes())
		if tx.XrpTx.Memos[0].Memo.MemoData != nil {
			buffer.Write(tx.XrpTx.Memos[0].Memo.MemoData)
		}

		return crypto.Keccak256(buffer.Bytes())
	}
	if tx.XrpType == rippleLib.TRUST_SET {
		var buffer bytes.Buffer
		buffer.Write(tx.PKBytes)
		buffer.Write(tx.XrpTrustSet.Account.Bytes())
		buffer.Write(tx.XrpTrustSet.LimitAmount.Bytes())

		return crypto.Keccak256(buffer.Bytes())
	}
	if tx.XrpType == rippleLib.ACCOUNT_DELETE {
		var buffer bytes.Buffer
		buffer.Write(tx.PKBytes)
		buffer.Write(tx.XrpAccountDelete.Account.Bytes())
		buffer.Write(tx.XrpAccountDelete.Destination.Bytes())
		if tx.XrpAccountDelete.Memos[0].Memo.MemoData != nil {
			buffer.Write(tx.XrpAccountDelete.Memos[0].Memo.MemoData)
		}

		return crypto.Keccak256(buffer.Bytes())
	}
	if tx.XrpType == rippleLib.ACCOUNT_SET {
		var buffer bytes.Buffer
		buffer.Write(tx.PKBytes)
		return crypto.Keccak256(buffer.Bytes())
	}
	return crypto.Keccak256([]byte{})
}

func (tx XrpTxHandler) String() string {
	return fmt.Sprintf("XrpTxHandler: %s", tx.TxData)
}
func (tx XrpTxHandler) GetHashedData() [][]byte {
	if tx.XrpType == rippleLib.ACCOUNT_DELETE {
		hash := *tx.XrpAccountDelete.GetHash()
		hash2 := tx.TxHash
		log.SyslogInfo("================================================== GetHashedData: %s, %s", hash, hash2)
		return [][]byte{hash[:]}
	}
	if tx.XrpType == rippleLib.PAYMENT {
		hash := *tx.XrpTx.GetHash()
		hash2 := tx.TxHash
		log.SyslogInfo("================================================== GetHashedData: %s, %s", hash, hash2)
		return [][]byte{hash[:]}
	}
	if tx.XrpType == rippleLib.TRUST_SET {
		hash := *tx.XrpTrustSet.GetHash()
		hash2 := tx.TxHash
		log.SyslogInfo("================================================== GetHashedData: %s, %s", hash, hash2)
		return [][]byte{hash[:]}
	}
	if tx.XrpType == rippleLib.ACCOUNT_SET {
		hash := *tx.XrpAccountSet.GetHash()
		hash2 := tx.TxHash
		log.SyslogInfo("================================================== GetHashedData: %s, %s", hash, hash2)
		return [][]byte{hash[:]}
	}
	return [][]byte{}
}

func GetXrpHandler(txHash hexutil.Bytes, TxData hexutil.Bytes) (*XrpTxHandler, error) {
	var tx XrpTxHandler
	tx.ChainType = "XRP"
	tx.TxHash = txHash
	tx.TxData = string(TxData)
	err := ParseXrp(TxData, &tx)
	if err != nil {
		log.SyslogInfo("GetXrpHandler, failed", "err", err)
		return nil, err
	}
	if tx.XrpType == rippleLib.PAYMENT {
		hash, _, err := rippleLib.SigningHash(&tx.XrpTx)
		if err != nil {
			log.SyslogInfo("SigningHash, failed", "err", err)
			return nil, err
		}
		*tx.XrpTx.GetHash() = hash
	}
	if tx.XrpType == rippleLib.TRUST_SET {
		hash, _, err := rippleLib.SigningHash(&tx.XrpTrustSet)
		if err != nil {
			log.SyslogInfo("SigningHash, failed", "err", err)
			return nil, err
		}
		*tx.XrpTrustSet.GetHash() = hash
	}
	if tx.XrpType == rippleLib.ACCOUNT_DELETE {
		hash, _, err := rippleLib.SigningHash(&tx.XrpAccountDelete)
		if err != nil {
			log.SyslogInfo("SigningHash, failed", "err", err)
			return nil, err
		}
		*tx.XrpAccountDelete.GetHash() = hash
	}
	if tx.XrpType == rippleLib.ACCOUNT_SET {
		hash, _, err := rippleLib.SigningHash(&tx.XrpAccountSet)
		if err != nil {
			log.SyslogInfo("SigningHash, failed", "err", err)
			return nil, err
		}
		*tx.XrpAccountSet.GetHash() = hash
	}
	return &tx, nil
}
func (tx XrpTxHandler) ValidateTx3() bool {
	return true
}
func (tx XrpTxHandler) ValidateTx() bool {
	log.SyslogInfo("Xrp ValidateTx, begin", "txInfo", tx.String())

	key := tx.GetKey()

	log.SyslogInfo("ValidateTx, begin", "key", key)
	followerDB, err := GetDB()
	if err != nil {
		log.SyslogErr("ValidateTx leader get database fail", "err", err.Error())
		return false
	}

	_, err = waitKeyFromDB([][]byte{key})
	if err != nil {
		log.SyslogErr("ValidateTx, check has fail", "key", key, "err", err.Error())
		return false
	}

	followerTxRawData, err := followerDB.Get(key)
	if err != nil {
		log.SyslogErr("ValidateTx, getting followerTxRawData fail", "err", err.Error())
		return false
	}

	log.SyslogInfo("ValidateTx, followerTxRawData is got")

	followerRawTx := XrpTxHandler{}
	err = ParseXrp(followerTxRawData, &followerRawTx)
	if err != nil {
		log.SyslogErr("ValidateTx, follower tx data decode fail", "err", err.Error())
		return false
	}

	if tx.XrpType == rippleLib.PAYMENT {
		if followerRawTx.XrpTx.TransactionType == tx.XrpTx.TransactionType &&
			followerRawTx.XrpTx.Destination == tx.XrpTx.Destination &&
			followerRawTx.XrpTx.Amount.Equals(tx.XrpTx.Amount) &&
			((followerRawTx.XrpTx.DestinationTag == nil && tx.XrpTx.DestinationTag == nil) ||
				(followerRawTx.XrpTx.DestinationTag != nil && tx.XrpTx.DestinationTag != nil && *followerRawTx.XrpTx.DestinationTag == *tx.XrpTx.DestinationTag)) &&
			followerRawTx.XrpTx.Account == tx.XrpTx.Account {

			log.SyslogInfo("ValidateTx, validate success", "data", tx.String())
			return true
		} else {
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.TransactionType", followerRawTx.XrpTx.TransactionType,
				"tx.XrpTx.TransactionType", tx.XrpTx.TransactionType, followerRawTx.XrpTx.TransactionType == tx.XrpTx.TransactionType)
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.Destination", followerRawTx.XrpTx.Destination,
				"tx.XrpTx.Destination", tx.XrpTx.Destination, followerRawTx.XrpTx.Destination == tx.XrpTx.Destination)
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.Amount", followerRawTx.XrpTx.Amount,
				"tx.XrpTx.Amount", tx.XrpTx.Amount, followerRawTx.XrpTx.Amount.Equals(tx.XrpTx.Amount))
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.Account", followerRawTx.XrpTx.Account,
				"tx.XrpTx.Account", tx.XrpTx.Account, followerRawTx.XrpTx.Account == tx.XrpTx.Account)
			if followerRawTx.XrpTx.DestinationTag != nil {
				log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.DestinationTag", *followerRawTx.XrpTx.DestinationTag)
			}
			if tx.XrpTx.DestinationTag != nil {
				log.SyslogErr("ValidateTx", "tx.XrpTx.DestinationTag", *tx.XrpTx.DestinationTag)
			}

			log.SyslogErr("ValidateTx", "followerRawTx", followerRawTx, "tx.XrpTx", tx.XrpTx)
			return false
		}
	}

	if tx.XrpType == rippleLib.ACCOUNT_DELETE {
		if followerRawTx.XrpAccountDelete.TransactionType == tx.XrpAccountDelete.TransactionType &&
			followerRawTx.XrpAccountDelete.Destination == tx.XrpAccountDelete.Destination {

			log.SyslogInfo("ValidateTx, validate success", "data", tx.String())
			return true
		} else {
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.TransactionType", followerRawTx.XrpAccountDelete.TransactionType,
				"tx.XrpTx.TransactionType", tx.XrpAccountDelete.TransactionType, followerRawTx.XrpAccountDelete.TransactionType == tx.XrpAccountDelete.TransactionType)
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.Destination", followerRawTx.XrpAccountDelete.Destination,
				"tx.XrpTx.Destination", tx.XrpAccountDelete.Destination, followerRawTx.XrpAccountDelete.Destination == tx.XrpAccountDelete.Destination)
			return false
		}
	}
	if tx.XrpType == rippleLib.ACCOUNT_SET {
		if followerRawTx.XrpAccountSet.TransactionType == tx.XrpAccountSet.TransactionType &&
			followerRawTx.XrpAccountSet.SetFlag != nil && tx.XrpAccountSet.SetFlag != nil &&
			*followerRawTx.XrpAccountSet.SetFlag == *tx.XrpAccountSet.SetFlag {

			log.SyslogInfo("ValidateTx, validate success", "data", tx.String())
			return true
		} else {
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.TransactionType", followerRawTx.XrpAccountSet.TransactionType,
				"tx.XrpTx.TransactionType", tx.XrpAccountSet.TransactionType, followerRawTx.XrpAccountSet.TransactionType == tx.XrpAccountSet.TransactionType)
			if followerRawTx.XrpAccountSet.SetFlag != nil && tx.XrpAccountSet.SetFlag != nil {
				log.SyslogErr("ValidateTx", "followerRawTx.XrpTx.SetFlag", *followerRawTx.XrpAccountSet.SetFlag,
					"tx.XrpTx.SetFlag", *tx.XrpAccountSet.SetFlag, *followerRawTx.XrpAccountSet.SetFlag == *tx.XrpAccountSet.SetFlag)
			}

			return false
		}
	}
	if tx.XrpType == rippleLib.TRUST_SET {
		if followerRawTx.XrpTrustSet.TransactionType == tx.XrpTrustSet.TransactionType &&
			followerRawTx.XrpTrustSet.LimitAmount.Equals(tx.XrpTrustSet.LimitAmount) &&
			followerRawTx.XrpTrustSet.Account == tx.XrpTrustSet.Account {

			log.SyslogInfo("ValidateTx, validate success", "data", tx.String())
			return true
		} else {
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTrustSet.TransactionType", followerRawTx.XrpTrustSet.TransactionType,
				"tx.XrpTrustSet.TransactionType", tx.XrpTrustSet.TransactionType, followerRawTx.XrpTrustSet.TransactionType == tx.XrpTrustSet.TransactionType)
			log.SyslogErr("ValidateTx", "followerRawTx.XrpTrustSet.LimitAmount", followerRawTx.XrpTrustSet.LimitAmount,
				"tx.XrpTrustSet.LimitAmount", tx.XrpTrustSet.LimitAmount, followerRawTx.XrpTrustSet.LimitAmount.Equals(tx.XrpTrustSet.LimitAmount))
			return false
		}
	}
	return false
}
